package com.mimo.logic.config.heartbeat;

import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ReflectionUtils;

import com.google.protobuf.StringValue;
import com.mimo.logic.session.service.ISessionService;
import com.mimo.logic.user.service.IUserService;

/**
 * 主要拦截com.mimo.logic包内所有@GrpcService的接口，捕获参数并推断出用户ID，进行心跳维护。
 *
 * 
 */
@Aspect
public class HeartbeatAspect {
  // 可能返回UID的方法名
  private static final Set<String> UID_GETTERS = new HashSet<>(Arrays.asList("getUserId", "getUid", "getFrom"));

  @Value("${logic.admin.name:SysAdmin}")
  private String adminName;
  @Autowired
  private ISessionService sessionService;
  @Autowired
  private IUserService userService;

  @Pointcut("@within(net.devh.boot.grpc.server.service.GrpcService)  && within(com.mimo.logic..grpc.*)  "
      + "&& !@within(com.mimo.logic.config.heartbeat.annotation.ExcludeHeartbeat) "
      + "&& !@annotation(com.mimo.logic.config.heartbeat.annotation.ExcludeHeartbeat) ")
  private void grpcServiceAsp() {
    // nothing
  }

  @Before("grpcServiceAsp()")
  public void doBefore(JoinPoint joinPoint) {
    String userId = extractUserId(joinPoint).filter(userService::isValid)
        .orElseThrow(() -> new IllegalArgumentException(
            String.format("HeartbeatAspect : not exist or illegal userId. Class[%s] Method[%s]",
                joinPoint.getTarget().getClass().getSimpleName(), joinPoint.getSignature().getName())));

    if (!Objects.equals(userId, adminName)) {
      sessionService.touch(userId, null);
    }

  }

  private Optional<String> extractUserId(JoinPoint joinPoint) {
    String possibleUserId = null;
    // 获取第一个protobuf参数
    Optional<Object> argOptional = Stream.of(joinPoint.getArgs())
        .filter(com.google.protobuf.MessageOrBuilder.class::isInstance).findFirst();
    // 尝试从参数中提取 UserId
    if (argOptional.isPresent()) {
      Object firstArgument = argOptional.get();
      if (StringValue.class.isInstance(firstArgument)) {
        possibleUserId = StringValue.class.cast(firstArgument).getValue();
      } else {
        possibleUserId = Stream.of(ReflectionUtils.getAllDeclaredMethods(firstArgument.getClass()))
            .filter(m -> UID_GETTERS.contains(m.getName()) && m.getReturnType() == java.lang.String.class
                && Modifier.isPublic(m.getModifiers()) && m.getParameterCount() == 0)
            .map(m -> (String) ReflectionUtils.invokeMethod(m, firstArgument)).findFirst().orElse(null);
      }
    }

    return Optional.ofNullable(possibleUserId);
  }
}
